home *** CD-ROM | disk | FTP | other *** search
- /* Bitmap.c: implementation of a Bitmap widget as a subclass of Athena's
- Label widget.
-
- Copyright (C) 1992 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include "config.h"
-
- #include "xt-common.h"
-
- #include "bitmap.h"
-
- #include "BitmapP.h"
-
- /* Says whether the rectangle we draw to represent the temporary
- selection is visible, i.e., if we've erased it. */
- static boolean selection_showing = false;
-
- /* Utility routines. */
- static Pixmap bitmap_to_pixmap (Display *, bitmap_type, unsigned);
- static void check_action_parameters (string, BitmapWidget, Cardinal, Cardinal);
- static boolean find_bitmap_position (XEvent *, BitmapWidget, int *, int *);
- static coordinate_type get_pointer_position (XEvent *);
- static unsigned param_value (string, string []);
- static void redraw_pixel (BitmapWidget, one_byte, long, int, int);
-
- /* Subroutines for handling selections. */
- static bounding_box_type find_selection_bb (BitmapWidget);
- static void redraw_selection_bitmap (BitmapWidget, bitmap_type);
- static void redraw_selection (BitmapWidget, bitmap_type);
- static void show_selection (BitmapWidget);
- static void unshow_selection (BitmapWidget);
- static void update_selection (BitmapWidget, unsigned, unsigned);
-
-
-
- /* Action routines. */
- static action_proc_type
- invert_pixel, fill_selection,
- start_selection, adjust_selection, accept_selection,
- start_paste, move_paste, accept_paste;
-
- /* The mapping of action names to procedures. */
- static XtActionsRec bitmap_actions[]
- = { { "InvertPixel", invert_pixel },
- { "FillSelection", fill_selection },
- { "StartSelection", start_selection },
- { "AdjustSelection", adjust_selection },
- { "AcceptSelection", accept_selection },
- { "StartPaste", start_paste },
- { "MovePaste", move_paste },
- { "AcceptPaste", accept_paste },
- };
-
-
- /* This structure defines the default values of the resources specific
- to the Bitmap widget. */
- #define OFFSET(field) XtOffset (BitmapWidget, bitmap.field)
- static XtResource bitmap_resources[]
- = { IMMEDIATE_RESOURCE (expansion, Expansion, Dimension,
- BITMAP_DEFAULT_EXPANSION),
- IMMEDIATE_RESOURCE (bits, Bitmap, Pointer, NULL),
- IMMEDIATE_RESOURCE (modified, Modified, Boolean, False),
- IMMEDIATE_RESOURCE (shadow, Bitmap, Widget, NULL),
- };
-
-
- /* Routines in the class record. */
- static void bitmap_initialize (Widget, Widget, ArgList, Cardinal *);
- static Boolean bitmap_set_values (Widget, Widget, Widget, ArgList, Cardinal *);
- static void bitmap_destroy (Widget);
-
- /* We can inherit most things in the instantiation of the class record.
- The operations `initialize', `set_values', and `destroy' are chained
- automatically by the toolkit, and need no `XtInherit...' constant
- (Xt manual, p.21). */
- BitmapClassRec bitmapClassRec
- = { /* Core class fields. */
- { /* superclass */ (WidgetClass) &labelClassRec,
- /* class_name */ "Bitmap",
- /* widget_size */ sizeof (BitmapRec),
- /* class_initialize */ NULL,
- /* class_part_initialize */ NULL,
- /* class_inited */ FALSE,
- /* initialize */ bitmap_initialize,
- /* initialize_hook */ NULL,
- /* realize */ XtInheritRealize,
- /* actions */ bitmap_actions,
- /* num_actions */ XtNumber (bitmap_actions),
- /* resources */ bitmap_resources,
- /* num_resources */ XtNumber (bitmap_resources),
- /* xrm_class */ NULLQUARK,
- /* compress_motion */ TRUE,
- /* compress_exposure */ TRUE,
- /* compress_enterleave */ TRUE,
- /* visible_interest */ FALSE,
- /* destroy */ bitmap_destroy,
- /* resize */ XtInheritResize,
- /* expose */ XtInheritExpose,
- /* set_values */ bitmap_set_values,
- /* set_values_hook */ NULL,
- /* set_values_almost */ XtInheritSetValuesAlmost,
- /* get_values_hook */ NULL,
- /* accept_focus */ NULL,
- /* version */ XtVersion,
- /* callback_private */ NULL,
- /* tm_table */ NULL,
- /* query_geometry */ XtInheritQueryGeometry,
- /* display_accelerator */ XtInheritDisplayAccelerator,
- /* extension */ NULL
- },
-
- /* Simple class fields. */
- { /* change_sensitive */ XtInheritChangeSensitive },
-
- /* Label class fields. */
- { 0 },
-
- /* Bitmap class fields. */
- { 0 }
- };
-
- WidgetClass bitmapWidgetClass = (WidgetClass) &bitmapClassRec;
-
-
-
- /* Class routines. */
-
- /* This routine is called at widget creation time by the toolkit, after
- our superclasses have been initialized. REQUEST is the widget that
- was originally requested by the user; NEW is the widget that has been
- created by our superclasses in response to the requests.
-
- We want to tell our Label superclass that it should deal with a pixmap,
- not with a string.
-
- We do not use the parameters ARGS and N_ARGS. */
-
- static void
- bitmap_initialize (Widget request, Widget new, ArgList args, Cardinal *n_args)
- {
- XGCValues select_gc_values;
- BitmapWidget old = xmalloc (sizeof (*old));
- BitmapWidget bw = (BitmapWidget) new;
- bitmap_type *bitmap_ptr = (bitmap_type *) bw->bitmap.bits;
- Arg bitmap_args[] = { { XtNbits, (XtArgVal) bitmap_ptr } };
- Cardinal n = XtNumber (bitmap_args);
- Display *d = XtDisplay (bw);
-
- if (bitmap_ptr == NULL)
- XtErrorMsg ("noData", "bitmapCreate", "BitmapError",
- "No bitmap specified.", NULL, 0);
-
- /* Convert the bitmap to a pixmap. We have to make sure that we
- really do create the pixmap here, by forcing the old and new
- bitmaps to be different. */
- *old = *(BitmapWidget) request;
- old->bitmap.bits = NULL;
- (void) bitmap_set_values ((Widget) old, request, new, bitmap_args, &n);
-
- /* Make GC's to use in editing the bits and doing the selection. */
- bw->bitmap.edit_gc = XCreateGC (XtDisplay (bw), bw->label.pixmap, 0, NULL);
- select_gc_values.function = GXxor;
- select_gc_values.foreground = AllPlanes;
- bw->bitmap.select_gc = XCreateGC (d, bw->label.pixmap,
- GCFunction | GCForeground,
- &select_gc_values);
-
- /* If we have a shadow bitmap, we want to use the same GC and bitmap
- for it as we do for the current one. We should probably do this in
- bitmap_set_values somewhere, also. */
- if (bw->bitmap.shadow != NULL)
- {
- BitmapWidget shadow_bw = (BitmapWidget) bw->bitmap.shadow;
- XFreeGC (d, shadow_bw->bitmap.edit_gc);
- shadow_bw->bitmap.edit_gc = bw->bitmap.edit_gc;
- }
-
- /* Initially, we're unmodified, and have no selection. */
- bw->bitmap.modified = false;
- bw->bitmap.selection = NULL;
- }
-
-
- /* This routine is called when one of the resources in the widget
- changes; for example, in response to an XtSetValues call. It is also
- called by the `bitmap_initialize' routine. The toolkit has already
- modified our resources. CURRENT is the widget before any resources
- were changed; REQUEST is the widget before any class `set_values'
- procedures have been called; NEW is the widget as updated by the
- superclasses.
-
- We do not use the parameters ARGS and N_ARGS. */
-
- static Boolean
- bitmap_set_values (Widget current, Widget request, Widget new,
- ArgList args, Cardinal *n_args)
- {
- BitmapWidget old_bw = (BitmapWidget) current;
- bitmap_type *old_bitmap_ptr = (bitmap_type *) old_bw->bitmap.bits;
- unsigned old_expansion = old_bw->bitmap.expansion;
- BitmapWidget bw = (BitmapWidget) new;
- bitmap_type *bitmap_ptr = (bitmap_type *) bw->bitmap.bits;
- unsigned expansion = bw->bitmap.expansion;
-
- if (bitmap_ptr == NULL)
- XtErrorMsg ("noData", "bitmapSetValues", "BitmapError",
- "No bitmap specified.", NULL, 0);
-
- if (bitmap_ptr == old_bitmap_ptr && expansion == old_expansion)
- return False; /* No redisplay needed. */
-
- {
- Display *display = XtDisplay (bw);
-
- /* Our new width and height, in window coordinates. */
- unsigned w = BITMAP_WIDTH (*bitmap_ptr) * expansion;
- unsigned h = BITMAP_HEIGHT (*bitmap_ptr) * expansion;
-
- /* The new image. */
- Pixmap pixmap = bitmap_to_pixmap (display, *bitmap_ptr, expansion);
-
- Arg label_args[]
- = { { XtNbitmap, pixmap },
- { XtNwidth, w },
- { XtNheight, h },
- };
-
- /* If we were already displaying a pixmap, free it. Perhaps it
- would be better to keep it around, in case the user goes back
- to this character. */
- if (bw->label.pixmap != None)
- XFreePixmap (display, bw->label.pixmap);
-
- /* The bitmap has changed, so we should clear the `modified' field. */
- bw->bitmap.modified = false;
-
- /* Make the changes. */
- XtSetValues ((Widget) bw, XTARG (label_args));
-
- /* We want to be redisplayed now. */
- return True;
- }
- }
-
-
- /* This routine is called when the widget is destroyed. We deallocate
- the GC resources we have explicitly created. */
-
- static void
- bitmap_destroy (Widget w)
- {
- BitmapWidget bw = (BitmapWidget) w;
-
- XFreeGC (XtDisplay (bw), bw->bitmap.edit_gc);
- XFreeGC (XtDisplay (bw), bw->bitmap.select_gc);
- }
-
-
-
- /* These convenience procedures save clients the trouble of constructing
- an ArgList to get the resources in the Bitmap widget. */
-
- unsigned
- BitmapExpansion (Widget w)
- {
- BitmapWidget bw = (BitmapWidget) w;
- return bw->bitmap.expansion;
- }
-
-
- bitmap_type *
- BitmapBits (Widget w)
- {
- BitmapWidget bw = (BitmapWidget) w;
- return (bitmap_type *) bw->bitmap.bits;
- }
-
-
- Boolean
- BitmapModified (Widget w)
- {
- BitmapWidget bw = (BitmapWidget) w;
- return bw->bitmap.modified;
- }
-
-
-
- /* This utility routine converts the bitmap B into a Pixmap on the
- display DISPLAY. Each pixel in B becomes EXPANSION pixels in the
- Pixmap. If the pixmap creation fails, it gives a fatal error. */
-
- #define DEPTH 1 /* We don't need grayscale or color bitmaps. */
-
- static Pixmap
- bitmap_to_pixmap (Display *display, bitmap_type b, unsigned expansion)
- {
- GC gc;
- XGCValues gc_values;
- unsigned row;
- XRectangle *save_rectangle_list;
- XRectangle *rectangle_list = NULL;
- unsigned nrectangles = 0;
-
- unsigned height = BITMAP_HEIGHT (b) * expansion;
- unsigned w = BITMAP_WIDTH (b);
- unsigned width = BITMAP_WIDTH (b) * expansion;
-
- /* We need a drawable to create the pixmap on. Since we know we have
- a root window, we don't have to be passed one. */
- Window root_window = DefaultRootWindow (display);
- Pixmap pixmap = XCreatePixmap (display, root_window, width, height, DEPTH);
-
- if (pixmap == None)
- FATAL ("bitmap_to_pixmap: Could not allocate pixmap");
-
- /* Start with the foreground zero (which is the default), since we
- want to clear the pixmap before writing anything. We want the
- background zero so that the gc will correspond to the bitmap. */
- gc_values.background = 0;
- gc = XCreateGC (display, pixmap, GCBackground, &gc_values);
-
- /* Write the bitmap to the pixmap. We want the one bits in the bitmap
- to end up as one bits in the pixmap, regardless of the actual
- foreground and background pixels in the application. Why? Because
- we (and the Label widget) use XCopyPlane to draw the pixmap, and
- XCopyPlane turns one bits into the foreground color, and zero bits
- in the background color. */
- XFillRectangle (display, pixmap, gc, 0, 0, width, height);
- XSetForeground (display, gc, 1L);
-
- /* Instead of creating a separate rectangle for each pixel, we
- combine adjacent pixels in each scanline into one rectangle. We
- could go further, and combine identical adjacent scanlines
- (analysis has shown this happens about 1/3 of the time). Or we
- could go further still, and use some theory about decomposition
- into a minimal set of rectangles, as cited in `Rectangular
- Convolution for Fast Filtering of Characters', by Avi Naiman and
- Alain Fournier, in the July 1987 SIGGRAPH Proceedings. But doing
- this simple job seems to make it fast enough. */
- for (row = 0; row < BITMAP_HEIGHT (b); row++)
- {
- unsigned start;
- boolean done = false;
- one_byte *row_data = BITMAP_ROW (b, row);
- unsigned *transitions = bitmap_find_transitions (row_data, w);
- unsigned *save_transitions = transitions;
-
- /* We must perform the test on `done' before dereferencing
- `transitions' in the increment step of the loop, because if we
- are indeed done, `transitions' may point to garbage.
- bitmap_find_transitions guarantees an even number of
- transitions. */
- for (start = *transitions++; start != w + 1;
- start = done ? w + 1 : *transitions++)
- {
- XRectangle *r;
- unsigned end = *transitions++;
-
- XRETALLOC (rectangle_list, ++nrectangles, XRectangle);
- r = &rectangle_list[nrectangles - 1];
- r->x = start * expansion;
- r->y = row * expansion;
- r->width = (end - start) * expansion;
- r->height = expansion;
- }
- free (save_transitions);
- }
-
- /* The protocol puts some limit on the number of rectangles that can
- be filled in a single request. It is not uncommon for us to have
- thousands of rectangles in the list, either because of a
- large font (e.g., cminch), or a high resolution. */
- #define MAX_NRECTANGLES 4000
- save_rectangle_list = rectangle_list;
- while (nrectangles > MAX_NRECTANGLES)
- {
- XFillRectangles (display, pixmap, gc, rectangle_list, MAX_NRECTANGLES);
- nrectangles -= MAX_NRECTANGLES;
- rectangle_list += MAX_NRECTANGLES;
- }
- XFillRectangles (display, pixmap, gc, rectangle_list, nrectangles);
- XFreeGC (display, gc);
-
- free (save_rectangle_list);
-
- return pixmap;
- }
-
-
- /* Redraw the pixel at (COL,ROW) in the bitmap widget BW. Update the
- pixmap in the widget, and, if realized, the window. */
-
- static void
- redraw_pixel (BitmapWidget bw, one_byte bitmap_color,
- long foreground_color, int row, int col)
- {
- static long last_foreground = 0; /* XCreateGC's default. */
- Display *display = XtDisplay (bw);
- unsigned expansion = bw->bitmap.expansion;
- int expanded_row = row * expansion,
- expanded_col = col * expansion;
- bitmap_type *bitmap = (bitmap_type *) bw->bitmap.bits;
- Pixmap label_pixmap = bw->label.pixmap;
-
- /* Update the bitmap structure. */
- BITMAP_PIXEL (*bitmap, row, col) = bitmap_color;
-
- /* Only set the foreground if it has changed, for efficiency. */
- if (foreground_color != last_foreground)
- {
- XSetForeground (display, bw->bitmap.edit_gc, foreground_color);
- last_foreground = foreground_color;
- }
- XFillRectangle (display, label_pixmap, bw->bitmap.edit_gc,
- expanded_col, expanded_row, expansion, expansion);
- bw->bitmap.modified = true;
-
- /* We may as well call XCopyPlane ourselves, instead of generating an
- Expose event, since we have all the information necessary. */
- if (XtIsRealized ((Widget) bw))
- XCopyPlane (display, label_pixmap, XtWindow (bw), bw->label.normal_GC,
- expanded_col, expanded_row, expansion, expansion,
- expanded_col, expanded_row, 1L);
- }
-
-
- /* Return the position of the pointer as recorded in EVENT. */
-
- static coordinate_type
- get_pointer_position (XEvent *event)
- {
- coordinate_type c;
-
- switch (event->type)
- {
- case MotionNotify:
- c.x = event->xmotion.x;
- c.y = event->xmotion.y;
- break;
-
- case ButtonPress:
- case ButtonRelease:
- c.x = event->xbutton.x;
- c.y = event->xbutton.y;
- break;
-
- case KeyPress:
- case KeyRelease:
- c.x = event->xkey.x;
- c.y = event->xkey.y;
- break;
-
- case EnterNotify:
- case LeaveNotify:
- c.x = event->xcrossing.x;
- c.y = event->xcrossing.y;
- break;
-
- default:
- c.x = 0;
- c.y = 0;
- }
-
- return c;
- }
-
-
- /* If SEEN != EXPECTED, or if there is no bitmap in BW to operate on,
- report an error for the action NAME. */
-
- static void
- check_action_parameters (string name, BitmapWidget bw, Cardinal seen,
- Cardinal expected)
- {
- if (bw->bitmap.bits == NULL || bw->label.pixmap == None)
- XtErrorMsg ("noData", name, "BitmapError",
- "No bitmap specified", NULL, 0);
-
- if (seen != expected)
- {
- string error_params[] = { itoa (expected), itoa (seen) };
- unsigned n_error_params = XtNumber (error_params);
- XtErrorMsg ("invalidParameters", name, "BitmapError",
- "Expected %s argument(s), found %s",
- error_params, &n_error_params);
- }
- }
-
-
- /* If V is not one of the elements in VALUE_LIST, abort with an error
- message. Otherwise, return the (zero-based) index of V in the list.
- VALUE_LIST should end with an element that is NULL. */
-
- static unsigned
- param_value (string v, string value_list[])
- {
- unsigned ret = 0;
-
- while (value_list[ret] != NULL && !STREQ (value_list[ret], v))
- ret++;
-
- /* Did we find it? */
- if (value_list[ret] == NULL)
- {
- unsigned this_value;
- unsigned n_error_params = 2;
- string error_params[n_error_params];
-
- error_params[0] = *value_list;
- for (this_value = 1; this_value < ret; this_value++)
- {
- string temp = error_params[0];
- error_params[0] = concat3 (error_params[0], ",",
- value_list[this_value]);
- free (temp);
- }
-
- error_params[1] = v;
-
- XtErrorMsg ("invalidParameter", "acceptPaste", "BitmapError",
- "Expected one of (%s), found `%s'",
- error_params, &n_error_params);
- }
-
- return ret;
- }
-
-
- /* Given a position in window coordinates, we sometimes need to
- translate it to an expanded pixel boundary. We want to do this via
- rounding, instead of truncating, so that the user can select the
- pixels in the rightmost column and bottommost row of the bitmap.
-
- On the other hand, sometimes we want to truncate the position,
- instead of rounding, so that the starting positions are intuitive.
-
- We assume that the variable `bw' points to a bitmap widget. */
-
- #define EXPANSION_ROUND(n) \
- (bw->bitmap.expansion \
- * (((n) + bw->bitmap.expansion - 1) / bw->bitmap.expansion))
-
- #define EXPANSION_TRUNC(n) \
- (bw->bitmap.expansion * ((n) / bw->bitmap.expansion))
-
-
- /* Return whether the cursor position in E is within the bitmap in BW.
- We assume that the bitmap is non-null. In any case, return the row
- and column in window coordinates in ROW and COL. */
-
- static boolean
- find_bitmap_position (XEvent *e, BitmapWidget bw, int *row, int *col)
- {
- coordinate_type pos = get_pointer_position (e);
- bitmap_type b = *(bitmap_type *) bw->bitmap.bits;
-
- /* Convert from the window coordinates to the pixel in the bitmap. */
- int bitmap_row = pos.y / bw->bitmap.expansion;
- int bitmap_col = pos.x / bw->bitmap.expansion;
-
- *row = pos.y;
- *col = pos.x;
-
- /* Return whether the row and column are outside the boundaries of the
- bitmap. */
- return pos.y >= 0 && bitmap_row < BITMAP_HEIGHT (b)
- && pos.x >= 0 && bitmap_col < BITMAP_WIDTH (b);
- }
-
-
-
- /* Action procedures. */
-
- /* N_PARAMS should be exactly one, and PARAMS should point to either the
- string `Continuous' or `Discrete' (case is significant). If the
- latter, we simply invert the pixel at the current mouse location, as
- specified in EVENT. If the former, we only do the inversion if we
- are at a different pixel on this call than we were on the previous
- one. */
-
- static void
- invert_pixel (Widget w, XEvent *event, String *params, Cardinal *n_params)
- {
- static int last_row = -1; /* Expanded pixel position of the last event. */
- static int last_col = -1;
-
- /* This is the color of the pixel at the pointer position at the time
- of the last `Discrete' call. */
- static one_byte source_pixel;
-
- int row, col;
-
- one_byte target_color;
- long foreground_color;
-
- BitmapWidget bw = (BitmapWidget) w;
- bitmap_type *bitmap = (bitmap_type *) bw->bitmap.bits;
- BitmapWidget shadow_bw = (BitmapWidget) bw->bitmap.shadow;
-
- check_action_parameters ("invertPixel", bw, *n_params, 1);
- if (!find_bitmap_position (event, bw, &row, &col))
- return;
-
- row /= bw->bitmap.expansion;
- col /= bw->bitmap.expansion;
-
- if (STREQ (*params, "Continuous"))
- { /* If we are still within the same (expanded) pixel, do nothing. */
- if (col == last_col && row == last_row)
- return;
-
- /* We've moved, so do the inversion. */
- last_col = col;
- last_row = row;
- }
- else if (STREQ (*params, "Discrete"))
- {
- /* Update `last_row' and `last_col' here, because it is easy to
- jiggle (inadvertently) the mouse a little after clicking. The
- jiggling will cause a motion event that will invoke us again with
- `Continuous' as the parameter. If we didn't update the current
- position here, that invocation would invert the pixel again. */
- last_col = col;
- last_row = row;
- source_pixel = BITMAP_PIXEL (*bitmap, row, col);
- }
- else
- {
- unsigned n_error_params = 1;
- string error_params[1] = { *params };
- XtErrorMsg ("invalidParameter", "invertPixel", "BitmapError",
- "Expected `Continuous' or `Discrete', found `%s'",
- error_params, &n_error_params);
- }
-
- /* See comments in `bitmap_to_pixmap' for why the constants 1 and 0
- are appropriate here. We depend on getting a `Discrete' event
- before any `Continuous' events; otherwise, `target_color' might be
- garbage.
-
- We are not testing the color of the pixel at the current
- position here. Why? Because we want to change pixels on
- `Continuous' events to a single color, not inverting them. See
- `Bitmap.h' for more discussion of this. */
- if (source_pixel == WHITE)
- { /* Make it black. */
- target_color = BLACK;
- foreground_color = 1;
- }
- else
- { /* Make it white. */
- target_color = WHITE;
- foreground_color = 0;
- }
-
- /* Update ourselves. */
- redraw_pixel (bw, target_color, foreground_color, row, col);
-
- /* Update our shadow bitmap, if we have one. */
- if (shadow_bw != NULL)
- redraw_pixel (shadow_bw, target_color, foreground_color, row, col);
- }
-
-
- /* Fill the selection in the bitmap widget W with the color of the pixel
- the pointer is on, as recorded in EVENT. N_PARAMS should be zero. */
-
- static void
- fill_selection (Widget w, XEvent *event, String *params, Cardinal *n_params)
- {
- int row, col; /* The coordinates in the bitmap. */
- BitmapWidget bw = (BitmapWidget) w;
-
- check_action_parameters ("fillSelection", bw, *n_params, 0);
- if (bw->bitmap.selection != NULL
- && bw->bitmap.select_width > 0 && bw->bitmap.select_height > 0
- && find_bitmap_position (event, bw, &row, &col))
- {
- bitmap_type new;
- bitmap_type *bitmap = (bitmap_type *) bw->bitmap.bits;
- one_byte color = BITMAP_PIXEL (*bitmap, row / bw->bitmap.expansion,
- col / bw->bitmap.expansion);
- bounding_box_type select_bb = find_selection_bb (bw);
-
- for (row = MIN_ROW (select_bb); row <= MAX_ROW (select_bb); row++)
- for (col = MIN_COL (select_bb); col < MAX_COL (select_bb); col++)
- BITMAP_PIXEL (*bitmap, row, col) = color;
-
- bw->bitmap.modified = true;
-
- new = extract_subbitmap (*bitmap, select_bb);
- redraw_selection (bw, new);
- free_bitmap (&new);
- }
- }
-
-
- /* Prepare to select a rectangle whose upper left corner is the pixel
- enclosing the position recorded in EVENT. N_PARAMS should be zero. */
-
- static void
- start_selection (Widget w, XEvent *event, String *params, Cardinal *n_params)
- {
- int row, col; /* The coordinates in the bitmap. */
- BitmapWidget bw = (BitmapWidget) w;
-
- check_action_parameters ("startSelection", bw, *n_params, 0);
- if (find_bitmap_position (event, bw, &row, &col))
- {
- /* Set the upper left corner and initialize the lower right corner to
- the current position. */
- bw->bitmap.initial_select.x = bw->bitmap.select_ul.x =
- EXPANSION_TRUNC (col);
- bw->bitmap.initial_select.y = bw->bitmap.select_ul.y =
- EXPANSION_TRUNC (row);
- bw->bitmap.select_width = bw->bitmap.select_height = 0;
- }
- }
-
-
- /* Take the point in EVENT as the new second point to define the
- selection. */
-
- static void
- adjust_selection (Widget w, XEvent *event, String *params, Cardinal *n_params)
- {
- int row, col; /* The coordinates in the bitmap. */
- BitmapWidget bw = (BitmapWidget) w;
-
- check_action_parameters ("adjustSelection", bw, *n_params, 0);
- unshow_selection (bw);
-
- if (find_bitmap_position (event, bw, &row, &col))
- {
- update_selection (bw, row, col);
- show_selection (bw);
- selection_showing = true;
- }
- }
-
-
- /* Take the point in EVENT to be the definitive second point for the
- selection. We therefore can now set the (sub)bitmap that the selected
- rectangle defines, for pasting. */
-
- static void
- accept_selection (Widget w, XEvent *event, String *params, Cardinal *n_params)
- {
- int row, col; /* The coordinates in the bitmap. */
- BitmapWidget bw = (BitmapWidget) w;
-
- check_action_parameters ("acceptSelection", bw, *n_params, 0);
- unshow_selection (bw);
-
- /* If the selection is empty, do nothing. Do not clear out the
- previous selection, even. */
- if (bw->bitmap.select_width > 0 && bw->bitmap.select_height > 0
- && find_bitmap_position (event, bw, &row, &col))
- {
- bounding_box_type select_bb;
-
- /* Set the final coordinates. */
- update_selection (bw, row, col);
-
- /* Free the old bitmap. */
- if (bw->bitmap.selection != NULL)
- free_bitmap (bw->bitmap.selection);
-
- /* Make the new one. */
- select_bb = find_selection_bb (bw);
- bw->bitmap.selection = xmalloc (sizeof (bitmap_type));
- *(bw->bitmap.selection)
- = extract_subbitmap (*(bitmap_type *) bw->bitmap.bits, select_bb);
- }
- }
-
-
- /* If something has been selected, show its bounding rectangle with the
- upper left corner at the position in EVENT (so the user can see where
- the paste will happen). */
-
- static void
- start_paste (Widget w, XEvent *event, String *params, Cardinal *n_params)
- {
- int row, col; /* The coordinates in the bitmap. */
- BitmapWidget bw = (BitmapWidget) w;
-
- check_action_parameters ("startPaste", bw, *n_params, 0);
- /* We obviously can't paste anything if nothing has been selected.
- Perhaps it would be better to show a warning message in that case,
- but for now we just do nothing. */
- if (bw->bitmap.selection != NULL
- && find_bitmap_position (event, bw, &row, &col))
- {
- bw->bitmap.select_ul.x = EXPANSION_TRUNC (col);
- bw->bitmap.select_ul.y = EXPANSION_TRUNC (row);
- show_selection (bw);
- selection_showing = true;
- }
- }
-
-
- /* Update where the paste rectangle is according to EVENT. */
-
- static void
- move_paste (Widget w, XEvent *event, String *params, Cardinal *n_params)
- {
- int row, col; /* The coordinates in the bitmap. */
- BitmapWidget bw = (BitmapWidget) w;
-
- check_action_parameters ("movePaste", bw, *n_params, 0);
- unshow_selection (bw);
-
- if (bw->bitmap.selection != NULL
- && find_bitmap_position (event, bw, &row, &col))
- {
- bw->bitmap.select_ul.x = EXPANSION_TRUNC (col);
- bw->bitmap.select_ul.y = EXPANSION_TRUNC (row);
- show_selection (bw); /* Show the new. */
- selection_showing = true;
- }
- }
-
-
- /* Take the position in EVENT as the place to paste the selection, and
- update the bitmap. Either ignore or keep the black in the bitmap
- underneath the paste according to first element of PARAMS, which must
- be either `Opaque' or `Transparent'. We flip the bitmap vertically,
- horizontally, both, or neither according to the second parameter
- being `FlipVertical', `FlipHorizontal', `FlipBoth', or `FlipNeither'. */
-
- static void
- accept_paste (Widget w, XEvent *event, String *params, Cardinal *n_params)
- {
- coordinate_type paste; /* The coordinates in the bitmap. */
- int row, col;
- BitmapWidget bw = (BitmapWidget) w;
-
- check_action_parameters ("acceptPaste", bw, *n_params, 2);
- unshow_selection (bw);
-
- if (bw->bitmap.selection != NULL
- && find_bitmap_position (event, bw, &row, &col))
- {
- enum { flip_neither, flip_horizontal, flip_vertical, flip_both } flip;
- boolean opaque;
- unsigned source_row, source_col;
- unsigned target_row, target_col;
- unsigned this_row, this_col;
- string param1_values[]
- = { "Transparent", "Opaque", NULL };
- string param2_values[]
- = { "FlipNeither", "FlipHorizontal", "FlipVertical", "FlipBoth",
- NULL };
-
- bitmap_type source = *(bw->bitmap.selection);
- unsigned source_height = BITMAP_HEIGHT (source),
- source_width = BITMAP_WIDTH (source);
- bitmap_type *target = (bitmap_type *) bw->bitmap.bits;
- unsigned target_height = BITMAP_HEIGHT (*target),
- target_width = BITMAP_WIDTH (*target);
-
- /* Get the parameters we're passed. */
- opaque = param_value (params[0], param1_values);
- flip = param_value (params[1], param2_values);
-
- /* Figure out where we are. */
- paste.x = col / bw->bitmap.expansion;
- paste.y = row / bw->bitmap.expansion;
-
- /* Update the underlying bitmap. Copy the black pixels from
- the original if we're pasting transparently. */
- for (this_row = 0, target_row = paste.y;
- this_row < source_height && target_row < target_height;
- this_row++, target_row++)
- {
- source_row = (flip == flip_horizontal || flip == flip_neither)
- ? this_row
- : source_height - this_row - 1;
-
- for (this_col = 0, target_col = paste.x;
- this_col < source_width && target_col < target_width;
- this_col++, target_col++)
- {
- source_col = (flip == flip_vertical || flip == flip_neither)
- ? this_col
- : source_width - this_col - 1;
-
- BITMAP_PIXEL (*target, target_row, target_col)
- = BITMAP_PIXEL (source, source_row, source_col)
- | (opaque
- ? WHITE
- : BITMAP_PIXEL (*target, target_row, target_col));
- }
- }
-
- bw->bitmap.modified = true;
-
- /* If necessary, get the combined selection image back. */
- if (!opaque || flip != flip_neither)
- {
- bounding_box_type select_bb = find_selection_bb (bw);
- source = extract_subbitmap (*target, select_bb);
- }
-
- redraw_selection (bw, source);
-
- if (!opaque)
- free_bitmap (&source);
- }
- }
-
-
-
- /* Convert the selection, which is in window coordinates, to a bounding
- box, in bitmap coordinates. */
-
- static bounding_box_type
- find_selection_bb (BitmapWidget bw)
- {
- bounding_box_type select_bb;
- unsigned expansion = bw->bitmap.expansion;
-
- MIN_COL (select_bb) = bw->bitmap.select_ul.x / expansion;
- MIN_ROW (select_bb) = bw->bitmap.select_ul.y / expansion;
- MAX_COL (select_bb)
- = MIN_COL (select_bb) + bw->bitmap.select_width / expansion;
- MAX_ROW (select_bb)
- = MIN_ROW (select_bb) + bw->bitmap.select_height / expansion;
-
- /* Because of the asymmetry of bounding boxes, we must
- subtract one from the row. */
- MAX_ROW (select_bb)--;
-
- return select_bb;
- }
-
-
- /* Redraw the area in the bitmap widget BW given by the selection. The
- new appearance of the area is given as a bitmap in SOURCE. Also
- redraw the shadow bitmap, if it exists. */
-
- #define COMPUTE_SHADOW_COORD(field) \
- shadow_bw->bitmap.field = shadow_bw->bitmap.expansion \
- * bw->bitmap.field / bw->bitmap.expansion;
-
- static void
- redraw_selection (BitmapWidget bw, bitmap_type source)
- {
- BitmapWidget shadow_bw = (BitmapWidget) bw->bitmap.shadow;
-
- redraw_selection_bitmap (bw, source);
-
- if (shadow_bw != NULL)
- {
- COMPUTE_SHADOW_COORD (select_ul.x);
- COMPUTE_SHADOW_COORD (select_ul.y);
- COMPUTE_SHADOW_COORD (select_width);
- COMPUTE_SHADOW_COORD (select_height);
- redraw_selection_bitmap (shadow_bw, source);
- }
- }
-
- static void
- redraw_selection_bitmap (BitmapWidget bw, bitmap_type source)
- {
- Display *display = XtDisplay (bw);
- Pixmap selection_pixmap = bitmap_to_pixmap (display, source,
- bw->bitmap.expansion);
-
- /* Update the pixmap in our parent label. */
- XCopyArea (XtDisplay (bw), selection_pixmap, bw->label.pixmap,
- bw->label.normal_GC, 0, 0,
- bw->bitmap.select_width, bw->bitmap.select_height,
- bw->bitmap.select_ul.x, bw->bitmap.select_ul.y);
- XFreePixmap (display, selection_pixmap);
-
- /* Update the display. */
- if (XtIsRealized ((Widget) bw))
- /* Server bug? On my machine, this doesn't always update large pastes
- correctly; some parts of the window are left. */
- #if 0
- XCopyPlane (display, bw->label.pixmap, XtWindow (bw),
- bw->label.normal_GC,
- bw->bitmap.select_ul.x, bw->bitmap.select_ul.y,
- bw->bitmap.select_width, bw->bitmap.select_height,
- bw->bitmap.select_ul.x, bw->bitmap.select_ul.y, 1L);
- #endif
- XClearArea (display, XtWindow (bw),
- bw->bitmap.select_ul.x, bw->bitmap.select_ul.y,
- bw->bitmap.select_width, bw->bitmap.select_height,
- True); /* Generate Expose event. */
- }
-
-
- /* If we are realized, display the currently selected rectangle. */
-
- static void
- show_selection (BitmapWidget bw)
- {
- if (XtIsRealized ((Widget) bw))
- XDrawRectangle (XtDisplay (bw), XtWindow (bw), bw->bitmap.select_gc,
- bw->bitmap.select_ul.x, bw->bitmap.select_ul.y,
- bw->bitmap.select_width, bw->bitmap.select_height);
- }
-
-
- /* If the user moves off the bitmap, we need to erase the last
- selection rectangle we drew. */
-
- static void
- unshow_selection (BitmapWidget bw)
- {
- if (selection_showing)
- {
- show_selection (bw);
- selection_showing = false;
- }
- }
-
-
- /* Do the appropriate maneuvers for the new row ROW and column COL for
- computing the upper left corner of the selection. These numbers are
- in unrounded window coordinates, i.e., they may not lie on pixel
- boundaries. If we think of the initial selection point as the origin,
- the new point can be in any of the four quadrants. */
-
- #define initial_x (bw->bitmap.initial_select.x)
- #define initial_y (bw->bitmap.initial_select.y)
-
- static void
- update_selection (BitmapWidget bw, unsigned row, unsigned col)
- {
- if (col < initial_x)
- {
- bw->bitmap.select_ul.x = EXPANSION_TRUNC (col);
- bw->bitmap.select_width = EXPANSION_ROUND (initial_x - col);
- }
- else
- {
- bw->bitmap.select_ul.x = initial_x;
- bw->bitmap.select_width = EXPANSION_ROUND (col - initial_x);
- }
-
- if (row < initial_y)
- {
- bw->bitmap.select_ul.y = EXPANSION_TRUNC (row);
- bw->bitmap.select_height = EXPANSION_ROUND (initial_y - row);
- }
- else
- {
- bw->bitmap.select_ul.y = initial_y;
- bw->bitmap.select_height = EXPANSION_ROUND (row - initial_y);
- }
- }
-